﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WangfsLib.Math
{
    /// <summary>
    /// 计算工具类
    /// </summary>
    public class MathUtils
    {
        /// <summary>
        /// 取绝对值
        /// </summary>
        /// <param name="num">要取绝对值的数</param>
        /// <returns>绝对值</returns>
        public static BigNumber Abs(BigNumber num)
        {
            if (num.Value[0] == '-') return num.Value[1..];
            return num;
        }

        /// <summary>
        /// 对大数开方
        /// </summary>
        /// <param name="num">要开方的大数</param>
        /// <returns>开方后的值</returns>
        /// <exception cref="NotFiniteNumberException">要开方的数必须是正数</exception>
        public static BigNumber Sqrt(BigNumber num)
        {
            if (num.Value == "0" || num.Value == "1") return num.Value;
            if (num.Value[0] == '-') throw new NotFiniteNumberException();
            string value = num.Value;

            int idx = value.IndexOf('.');
            int cnt = idx < 0 ? value.Length : idx;
            List<char> list = new();
            string tmp = value.Substring(0, 2 - (cnt & 1));
            char a;
            BigNumber n;
            int offset = tmp.Length;
            do
            {
                cnt -= offset;
                offset = 2;
                a = Sqrt_try(tmp, new string(list.ToArray()), out _);
                list.Add(a);
                if (cnt > 0) tmp = value.Substring(0, tmp.Length + 2);
            } while (cnt > 0);
            if (idx > 0)
            {
                list.Add('.');
                cnt = value.Length - idx - 1;
                offset = 3;
                cnt >>= 1;
                cnt <<= 1;
                while (cnt > 0)
                {
                    tmp = value.Substring(0, tmp.Length + offset);
                    a = Sqrt_try(tmp, new string(list.ToArray()), out _);
                    list.Add(a);
                    offset = 2;
                    cnt -= 2;
                }
                tmp = value;
            }
            else
            {
                n = new string(list.ToArray());
                if (value == n * n) return n;
                list.Add('.');
                tmp = value;
            }
            cnt = 0;
            while (cnt < BigNumber.PRECISION)
            {
                a = Sqrt_try(tmp, new string(list.ToArray()), out bool eqs);
                list.Add(a);
                if (eqs) break;
                cnt++;
            }

            return new string(list.ToArray());
        }

        static char Sqrt_try(string num, string val, out bool equals)
        {
            BigNumber n, x;
            char a = '0';
            equals = false;
            do
            {
                if (a == 58)
                {
                    a++;
                    break;
                }
                n = val + a;
                a++;
                x = num - n * n;
                equals = x == BigNumber.Zero;
            } while (equals || x > BigNumber.Zero);
            a--;
            a--;
            return a;
        }

        /// <summary>
        /// 计算整数 a b 的最大公约数
        /// </summary>
        /// <param name="a">整数 a</param>
        /// <param name="b">整数 b</param>
        /// <returns>a 与 b 的最大公约数</returns>
        public static int Gcd(int a, int b)
        {
            while (b != 0)
                (a, b) = (b, a % b);
            return a;
        }
        /// <summary>
        /// 计算长整数 a b 的最大公约数
        /// </summary>
        /// <param name="a">长整数 a</param>
        /// <param name="b">长整数 b</param>
        /// <returns>a 与 b 的最大公约数</returns>
        public static long Gcd(long a, long b)
        {
            while (b != 0)
                (a, b) = (b, a % b);
            return a;
        }
        /// <summary>
        /// 计算单精度 a b 的最大公约数
        /// </summary>
        /// <param name="a">单精度 a</param>
        /// <param name="b">单精度 b</param>
        /// <returns>a 与 b 的最大公约数</returns>
        public static float Gcd(float a, float b)
        {
            while (b != 0)
                (a, b) = (b, a % b);
            return a;
        }
        /// <summary>
        /// 计算双精度 a b 的最大公约数
        /// </summary>
        /// <param name="a">双精度 a</param>
        /// <param name="b">双精度 b</param>
        /// <returns>a 与 b 的最大公约数</returns>
        public static double Gcd(double a, double b)
        {
            while (b != 0)
                (a, b) = (b, a % b);
            return a;
        }
        /// <summary>
        /// 计算大数 a b 的最大公约数
        /// </summary>
        /// <param name="a">大数 a</param>
        /// <param name="b">大数 b</param>
        /// <returns>a 与 b 的最大公约数</returns>
        public static BigNumber Gcd(BigNumber a, BigNumber b)
        {
            while (b.Value != "0")
                (a, b) = (b, a % b);
            return a;
        }

    }
}
